#include <cstdlib>
#include <cstdio>
#include <math.h>
#include "Image.h"

#pragma pack(push,1)

#define PSIZE sizeof(unsigned char*)
#define FH sizeof(BITMAPFILEHEADER)
#define IH sizeof(BITMAPINFOHEADER)

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;

extern unsigned char* temp;

typedef struct tagBITMAPFILEHEADER  //bmp文件头
{
    WORD       bfType;          //文件类型(BM)
    DWORD      bfSize;          //文件大小(字节)
    WORD       bfReserved1;     //文件保留字
    WORD       bfReserved2;     //文件保留字
    DWORD      OffBits;         //文件数据距文件头的偏移量(字节)

}BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER  //bmp信息头
{
	DWORD      biSize;          //本结构所占字节:40
	DWORD      biWidth;         //位图宽度(像素)
	DWORD      biHeight;        //位图高度(像素)
	WORD       biPlanes;        //目标设备级别:1
	WORD       biBitCount;      //每个像素所需位数:1(双色),4(16色),8(256色),24(真彩色)
	DWORD      biCompression;   //位图压缩类型:0(BI_RGB不压缩),1(BI_RLE8压缩),2(BI_RLE压缩)
	DWORD      biSizeImage;     //位图大小(字节)
	DWORD      biXPelsPerMeter; //水平分辨率(像素/米)
	DWORD      biYPelsPerMeter; //垂直分辨率(像素/米)
	DWORD      biClrUsed;       //实际使用的颜色数
	DWORD      biClrImportant;  //显示过程中重要的颜色数

}BITMAPINFOHEADER;

inline int change(int x, int max)
{
    return (x>0?x:0)>=max?max-1:(x>0?x:0);
}
inline int N(int n)
{
    return n>0?n:-n;
}

//构造函数
Image::Image() :height(0), width(0)
{
    data = NULL;
}

Image::Image(int h, int w) :height(h),width(w)
{
    data = (BYTE**)malloc(h*PSIZE);
    for(int i=0; i<height; i++)
    {
        data[i] = (BYTE*)malloc(w*3);
        for(int j=0; j<width; j++)
        {
            data[i][j*3] = data[i][j*3+1] = data[i][j*3+2] = 0;
        }
    }
}

Image::Image(const char* ImageName)
{
    FILE* fp = NULL;
    fp = fopen(ImageName, "rb");
    if(fp == NULL)
        {
            printf("File Error: check the file %s\n",ImageName);
            system("pause");exit(0);
        }
    BITMAPFILEHEADER bmpf;
    BITMAPINFOHEADER bmpi;
    fread(&bmpf, FH, 1, fp);
    fread(&bmpi, IH, 1, fp);
    height = bmpi.biHeight;
    width = bmpi.biWidth;
    data = (BYTE**)malloc(height*PSIZE);
    BYTE* unuse = (BYTE*)malloc(4-width*3%4);
    for(int i=0; i<height; i++)
    {
        data[i] = (BYTE*)malloc(width*3);
        fread(data[i], width*3, 1, fp);
        if(width*3%4!=0)
            fread(unuse, 4-width*3%4, 1, fp);
    }
    fclose(fp);
}

//拷贝构造函数
Image::Image(const Image &im)
{
    height = im.height;
    width = im.width;
    data = (BYTE**)malloc(height*PSIZE);
    for(int i=0; i<height; i++)
    {
        data[i] = (BYTE*)malloc(width*3);
        for(int j=0; j<width; j++)
        {
            data[i][j*3] = im.data[i][j*3];
            data[i][j*3+1] = im.data[i][j*3+1];
            data[i][j*3+2] = im.data[i][j*3+2];
        }
    }
}

//析构函数
Image::~Image()
{
    if(data != NULL)
        for(int i=0; i<height; i++)
        {
            free(data[i]);
        }
    free(data);
}

Image& Image::operator=(const Image &im)
{
    if(height != im.height && width != im.width)
    {
        if(data != NULL)
            for(int i=0; i<height; i++)
                free(data[i]);
        free(data);
        height = im.height;
        width = im.width;
        data = (BYTE**)malloc(height*PSIZE);
        for(int i=0; i<height; i++)
        {
            data[i] = (BYTE*)malloc(width*3);
            for(int j=0; j<width; j++)
            {
                data[i][j*3] = im.data[i][j*3];
                data[i][j*3+1] = im.data[i][j*3+1];
                data[i][j*3+2] = im.data[i][j*3+2];
            }
        }
    }
    else
    {
        for(int i=0; i<height; i++)
        {
            for(int j=0; j<width; j++)
            {
                data[i][j*3] = im.data[i][j*3];
                data[i][j*3+1] = im.data[i][j*3+1];
                data[i][j*3+2] = im.data[i][j*3+2];
            }
        }
    }
    return *this;
}

//类型转换函数
Image::operator QImage(){
    free(temp);
    temp=(BYTE*)malloc(width*height*3);
    for(int i=height-1,k=0;i>=0;i--){
        for(int j=0;j<width*3;j++){
            temp[k]=data[i][j];
            k++;
        }
    }
    QImage img(temp,width,height,width*3,QImage::Format_BGR888);
    return img;
}

//从硬盘读入图像文件; 
bool Image::ReadBMP(const char* filename)
{
	FILE* fp = NULL;
    fp = fopen(filename, "rb");
    if(fp == NULL)
    {
        return false;
    }
    if(data != NULL)
        for(int i=0; i<height; i++)
            free(data[i]);
    free(data);
    BITMAPFILEHEADER bmpf;
    BITMAPINFOHEADER bmpi;
    fread(&bmpf, FH, 1, fp);
    fread(&bmpi, IH, 1, fp);
    height = bmpi.biHeight;
    width = bmpi.biWidth;
    data = (BYTE**)malloc(height*PSIZE);
    BYTE* unuse = (BYTE*)malloc(4-width*3%4);
    for(int i=0; i<height; i++)
    {
        data[i] = (BYTE*)malloc(width*3);
        fread(data[i], width*3, 1, fp);
        if(width*3%4!=0)
            fread(unuse, 4-width*3%4, 1, fp);
    }
    fclose(fp);
    return true;
}

//保存图像; 
bool Image::WriteBMP(const char *filename)
{
	FILE* fp = NULL;
    fp = fopen(filename, "wb");
    if(fp == NULL)
    {
        return false;
    }
    BITMAPFILEHEADER bmpf;
    BITMAPINFOHEADER bmpi;
    bmpi.biHeight = height;
    bmpi.biWidth = width;
    bmpi.biSizeImage = 0;
    bmpi.biSize = IH;
    bmpi.biBitCount = 24;
    bmpi.biPlanes = 1;
    bmpi.biCompression = 0;
    bmpi.biXPelsPerMeter = bmpi.biYPelsPerMeter = 3780;
    bmpi.biClrImportant = bmpi.biClrUsed = 0;
    bmpf.bfSize = FH+IH+height*((width*3+3)/4*4);
    bmpf.bfReserved1 = bmpf.bfReserved2 = 0;
    bmpf.OffBits = FH+IH;
    bmpf.bfType = 0x4D42;
    fwrite(&bmpf, FH, 1, fp);
    fwrite(&bmpi, IH, 1, fp);
    BYTE temp = 0;
    if(width%4 == 0)
    {
        for(int i=0; i<height; i++)
            fwrite(data[i], width*3, 1, fp);
    }
    else
    {
        for(int i=0; i<height; i++)
        {
            fwrite(data[i], width*3, 1, fp);
            temp = (BYTE)0;
            fwrite(&temp, 1, 4-width*3%4, fp);
        }
    }
    fclose(fp);
    printf("%s output successed\n", filename);
    return true;
}

int Image::Height()const
{
    return height;
}
int Image::Width()const
{
    return width;
}

BYTE& Image::Set(int row, int col)
{
    return data[row][col];
}
BYTE Image::At(int row, int col)const
{
    return data[row][col];
}

int Image::SquareArray(int row, int col, int size, BYTE** ptr)const
{
    (*ptr) = (BYTE*)malloc(size*size);
    int temp = 0;
    for(int p=-size/2; p<=size/2; ++p)
    {
        if(row+p<0 || row+p>=height) continue;
        for(int q=-size/2*3; q<=size/2*3; q+=3)
        {
            if(col+q<0 || col+q>=width*3) continue;
            (*ptr)[temp++] = data[row+p][col+q];
        }
    }
    return temp;
}

void Image::Rotate()
{
    BYTE** newdata = (BYTE**)malloc(PSIZE*width);
    for(int i=0; i<width; ++i)
    {
        newdata[i] = (BYTE*)malloc(height*3);
        for(int j=0; j<height; ++j)
        {
            newdata[i][j*3+0] = data[(height-1-j)][i*3+0];
            newdata[i][j*3+1] = data[(height-1-j)][i*3+1];
            newdata[i][j*3+2] = data[(height-1-j)][i*3+2];
        }
    }
    for(int i=0; i<height; ++i)
        free(data[i]);
    free(data);
    height = height+width;
    width = height-width;
    height = height-width;
    data = newdata;
}

void Image::Rotate2()
{
    BYTE** newdata = (BYTE**)malloc(PSIZE*width);
    for(int i=0; i<width; ++i)
    {
        newdata[i] = (BYTE*)malloc(height*3);
        for(int j=0; j<height; ++j)
        {
            newdata[i][j*3+0] = data[j][(width-1-i)*3+0];
            newdata[i][j*3+1] = data[j][(width-1-i)*3+1];
            newdata[i][j*3+2] = data[j][(width-1-i)*3+2];
        }
    }
    for(int i=0; i<height; ++i)
        free(data[i]);
    free(data);
    height = height+width;
    width = height-width;
    height = height-width;
    data = newdata;
}


void Image::Flip_LR()
{
    BYTE temp=0;
    for(int i=0; i<height; ++i)
    {
        for(int j=0; j<width/2; ++j)
        {
            for(int rgb=0; rgb<3; ++rgb)
            {
                temp = data[i][j*3+rgb];
                data[i][j*3+rgb] = data[i][(width-1-j)*3+rgb];
                data[i][(width-1-j)*3+rgb] = temp;
            }
        }
    }
}

void Image::Flip_UD()
{
    BYTE* temp=NULL;
    for(int i=0; i<height/2; ++i)
    {
        temp = data[i];
        data[i] = data[height-1-i];
        data[height-1-i] = temp;
    }
}

void Image::BrightLine(double alpha, int beta)
{
    int temp = 0;
    for(int i=0; i<height; ++i)
    {
        for(int j=0; j<width; ++j)
        {
            for(int rgb=0; rgb<3; ++rgb)
            {
                temp = (data[i][j*3+rgb]-128)*alpha+128+beta;
                temp = temp>0?temp:0;
                data[i][j*3+rgb] = temp>255?255:temp;
            }
        }
    }
}


void Image::GrayChange(double percent)
{
    double average;
    int temp = 0;
    for(int i=0; i<height; ++i)
    {
        for(int j=0; j<width; ++j)
        {
            average = (data[i][j*3]+data[i][j*3+1]+data[i][j*3+2])/3.0;
            for(int rgb=0; rgb<3; ++rgb)
            {
                temp = data[i][j*3+rgb]+(average-data[i][j*3+rgb])*percent;
                temp = temp>0?temp:0;
                data[i][j*3+rgb] = temp>255?255:temp;
            }
        }
    }
}

void Image::Inversion()
{
    for(int i=0; i<height; ++i)
    {
        for(int j=0; j<width; ++j)
        {
            for(int rgb=0; rgb<3; ++rgb)
            {
                data[i][j*3+rgb] = 255-data[i][j*3+rgb];
            }
        }
    }
}

void Image::Sgmtation(int delta,int r,int g,int b)
{
    int temp=0;
    for(int i=0; i<height; ++i)
    {
        for(int j=0; j<width; ++j)
        {
           temp=(data[i][j*3]+data[i][j*3+1]+data[i][j*3+2])/3;
           if(temp<delta)
           {
               data[i][j*3]=b;
               data[i][j*3+1]=g;
               data[i][j*3+2]=r;
           }
           else
               data[i][j*3]=data[i][j*3+1]=data[i][j*3+2]=255;
        }
    }
}


void Image:: Colortemperature (int epsilon)
{

    for(int i=0; i<height; ++i)
    {
        for(int j=0; j<width; ++j)
        {
            data[i][j*3] = change(data[i][j*3]-epsilon,256);
            data[i][j*3+1] = change(data[i][j*3+1]+epsilon, 256);
            data[i][j*3+2] = change(data[i][j*3+2]+epsilon, 256);
        }
    }
}

void Image::Woodcut(int num){
    for(int i=0;i<height;i++){
        for(int j=0;j<width;j++){
            if(i+j!=0&&j!=0&&abs(data[i][j*3+1]-data[i][j*3-2])<num&&abs(data[i][j*3+2]-data[i][j*3-1])<num&&abs(data[i][j*3+2]-data[i][j*3])<num){
                data[i][j*3+1]=data[i][j*3-2];
                data[i][j*3+2]=data[i][j*3-1];
                data[i][j*3+3]=data[i][j*3];
            }
            else if(i!=0&&i+j!=0&&j>0&&abs(data[i][j*3+1]-data[i-1][j*3+-2])<num&&abs(data[i][j*3+2]-data[i-1][j*3-1])<num&&abs(data[i][j*3+3]-data[i-1][j*3])<num){
                data[i][j*3+1]=data[i-1][j*3-2];
                data[i][j*3+2]=data[i-1][j*3-1];
                data[i][j*3+3]=data[i-1][j*3];
            }
            else if(i!=0&&i+j!=0&&abs(data[i][j*3+1]-data[i-1][j*3+1])<num&&abs(data[i][j*3+2]-data[i-1][j*3+2])<num&&abs(data[i][j*3+3]-data[i-1][j*3+3])<num){
                data[i][j*3+1]=data[i-1][j*3+1];
                data[i][j*3+2]=data[i-1][j*3+2];
                data[i][j*3+3]=data[i-1][j*3+3];
            }
            else if(i!=0&&i+j!=0&&j<width-1&&abs(data[i][j*3+1]-data[i-1][j*3+4])<num&&abs(data[i][j*3+2]-data[i-1][j*3+5])<num&&abs(data[i][j*3+3]-data[i-1][j*3+6])<num){
                data[i][j*3+1]=data[i-1][j*3+4];
                data[i][j*3+2]=data[i-1][j*3+5];
                data[i][j*3+3]=data[i-1][j*3+6];
            }
        }
    }
}

void Image::CoulorLayers(int eta)
{
    for(int i=0; i<height; ++i)
    {
        for(int j=0; j<width; ++j)
        {
            for(int rgb=0; rgb<3; ++rgb)
            {
                data[i][j*3+rgb] = (data[i][j*3+rgb] + eta/2)/eta*eta;
            }
        }
    }
}

void Image::FindEdge(int epsilon, int r, int g, int b)
{
    Image *img;
    img = new Image(*this);
    img->GrayChange(1);
    //img->CoulorLayers(8);
    int check = 0;
    int n1, n2;
    for(int i=0;i<height;i++)
    {
        for(int j=0;j<width;j++)
        {
            n1 = img->At(change(i+1,height),change(j-1,width)*3)-img->At(change(i-1,height),change(j-1,width)*3)
                +img->At(change(i+1,height),change(j,width)*3)*2-img->At(change(i-1,height),change(j,width)*3)*2
                +img->At(change(i+1,height),change(j+1,width)*3)-img->At(change(i-1,height),change(j+1,width)*3);
            n2 = img->At(change(i+1,height),change(j+1,width)*3)-img->At(change(i+1,height),change(j-1,width)*3)
                +img->At(change(i,height),change(j+1,width)*3)*2-img->At(change(i,height),change(j-1,width)*3)*2
                +img->At(change(i-1,height),change(j+1,width)*3)-img->At(change(i-1,height),change(j-1,width)*3);
            check = N(n1)+N(n2);
            if(check>epsilon)
            {
                data[i][j*3] = b;
                data[i][j*3+1] = g;
                data[i][j*3+2] = r;
            }
        }
    }
    delete img;

}

void Image::Sharpen(double zeta)
{
    Image *img;
    img = new Image(*this);
    img->GrayChange(1);
    int check = 0;
    for(int i=0;i<height;i++)
    {
        for(int j=0;j<width;j++)
        {
            check = img->At(change(i,height),change(j,width)*3)*4
                -img->At(change(i+1,height),change(j,width)*3)-img->At(change(i-1,height),change(j,width)*3)
                -img->At(change(i,height),change(j+1,width)*3)-img->At(change(i,height),change(j-1,width)*3);
            if(check>20)
            {
                data[i][j*3] = change(data[i][j*3]*zeta, 256);
                data[i][j*3+1] = change(data[i][j*3+1]*zeta, 256);
                data[i][j*3+2] = change(data[i][j*3+2]*zeta, 256);
            }
            else if(check<-20)
            {
                data[i][j*3] = change(data[i][j*3]/zeta, 256);
                data[i][j*3+1] = change(data[i][j*3+1]/zeta, 256);
                data[i][j*3+2] = change(data[i][j*3+2]/zeta, 256);
            }
        }
    }
    delete img;
}
